﻿#include "ctablemodel.h"

/**
 * @brief 构造函数
 * @param parent 父对象
 */
CTableModel::CTableModel(QObject *parent):QAbstractTableModel(parent)
{

}

/**
 * @brief 添加数据到模型
 * @param data 要添加的数据项，使用QSharedPointer封装的IModel接口实例
 */
void CTableModel::appendData(QSharedPointer<IModel> data)
{
    auto bEmpty =  m_dataList.size() == 0;
    if(bEmpty)
        emit layoutAboutToBeChanged();
    beginInsertRows(QModelIndex(),m_dataList.size(),m_dataList.size());
    m_dataList.append(data);
    endInsertRows();
    if(bEmpty)
        emit layoutChanged();
}

/**
 * @brief 删除指定索引的数据项
 * @param index 要删除数据的索引位置
 */
void CTableModel::removeData(int index)
{
    // 检查索引是否有效，并执行删除操作
    if(index>=0&&index<m_dataList.size())
    {
        emit layoutAboutToBeChanged(); // 准备更新布局
        m_dataList.removeAt(index);
        emit layoutChanged(); // 布局已更新
    }
}

void CTableModel::removeAllData()
{
    emit layoutAboutToBeChanged(); // 准备更新布局
    m_dataList.clear(); // 从列表中移除匹配项
    emit layoutChanged(); // 布局已更新
}

/**
 * @brief 移除模型数据
 * 该函数通过传入的数据列表，逐个调用removeData函数移除指定的数据。
 * 
 * @param dataList 要移除的数据列表，列表中的元素是指向IModel接口的智能指针。
 */
void CTableModel::removeData(const QList<QSharedPointer<IModel> > &dataList)
{
    // 遍历数据列表，并对每个数据调用removeData进行移除
    for(auto data:dataList)
        removeData(data);
}

/**
 * 从模型中移除指定的数据项。
 * @param data 指向要移除的数据项的智能指针。
 * 这个函数会遍历模型的数据列表，找到与给定数据项ID匹配的项，并将其从列表中移除。
 * 当数据列表发生变化时，会发出相应的信号通知界面更新。
 */
void CTableModel::removeData(const QSharedPointer<IModel> &data)
{
    // 遍历数据列表，寻找与给定数据ID匹配的项
    for(auto d:m_dataList)
    {
        if(d->id == data->id) // 找到匹配项
        {
            emit layoutAboutToBeChanged(); // 准备更新布局
            m_dataList.removeOne(d); // 从列表中移除匹配项
            emit layoutChanged(); // 布局已更新
            break;
        }
    }
}

/**
 * @brief 获得行数
 */
int CTableModel::rowCount()
{
    return m_dataList.size();
}

/**
 * @brief 通过索引获取数据
 * 
 * 此函数通过给定的索引从数据列表中获取对应的数据项。如果索引有效，则返回对应项的智能指针；否则，返回空指针。
 * 
 * @param index 请求的数据项索引。
 * @return QSharedPointer<IModel> 如果索引有效，返回对应数据项的智能指针；否则返回空指针。
 */
QSharedPointer<IModel> CTableModel::getDataByIndex(int index)
{
    // 检查索引是否有效
    if(index>=0 && index<m_dataList.size())
    {
        return m_dataList.at(index); // 返回对应索引的数据项
    }
    return nullptr; // 索引无效，返回空指针
}

/**
 * @brief 获取行数
 * @param parent 索引的父对象
 * @return 返回行数
 */
int CTableModel::rowCount(const QModelIndex &parent) const
{
    // 返回数据列表的大小作为行数
    return m_dataList.size();
}

/**
 * @brief 获取列数
 * @param parent 索引的父对象
 * @return 返回列数，如果数据列表为空，返回0
 */
int CTableModel::columnCount(const QModelIndex &parent) const
{
    // 如果数据列表非空，返回第一项的字段数，否则返回0
    if(m_dataList.size()>0)
        return m_dataList.at(0)->fieldCount();
    else
        return 0;
}

/**
 * @brief 获取指定索引处的数据
 * @param index 指定的数据索引
 * @param role 数据的角色
 * @return 返回对应索引和角色的数据
 */
QVariant CTableModel::data(const QModelIndex &index, int role) const
{
    // 处理显示角色的数据请求
    if(Qt::DisplayRole == role)
    {
        auto row = index.row();
        auto col = index.column();
        // 检查索引是否有效
        if(m_dataList.size()<row)
            return QVariant();
        auto fieldName = m_dataList.at(row)->getFieldNameByCol(col);
        auto value = m_dataList.at(row)->getFieldValueByFielddName(fieldName);

        // 检查并处理值的描述
        {
            if(QVariant::String == value.type())
                return value;
            QMap<int, QString> valuesDesMap;
            if(hasValusDes(index,valuesDesMap) && value.canConvert<int>())
            {
                auto valueInt = value.toInt();
                if(valuesDesMap.contains(valueInt))
                    return valuesDesMap[valueInt];
            }
        }

        return value;
    }
    // 处理用户角色的数据请求
    else if(Qt::UserRole == role)
    {
        auto row = index.row();
        if(m_dataList.size()<row)
            return QVariant();
        return QVariant::fromValue(m_dataList.at(row));
    }
    else if(Qt::TextAlignmentRole == role) {
        return Qt::AlignCenter;
    }
    else
        return QVariant();
}

bool CTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    auto vdata = index.data(Qt::UserRole);
    auto data = qvariant_cast<QSharedPointer<IModel>>(vdata);//转IModel
    if(!data)
        return false;

    auto col = index.column();

    data->setFieldValueByCol(col,value);
    return true;
}

/**
 * @brief 获取指定索引处的头信息
 * @param section 索引的段落
 * @param orientation 索引的朝向
 * @param role 数据的角色
 * @return 返回对应索引、朝向和角色的头信息
 */
QVariant CTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    // 处理显示角色的垂直头信息请求
    if(Qt::DisplayRole == role)
    {
        if(Qt::Vertical == orientation)
            return QAbstractTableModel::headerData(section,orientation,role);
        else if(Qt::Horizontal == orientation)
        {
            if(m_dataList.size()>0)
                return m_dataList.at(0)->getFieldNameByCol(section);
        }
    }
    else{
        return QAbstractTableModel::headerData(section,orientation,role);
    }
    return QAbstractTableModel::headerData(section,orientation,role);
}

/**
 * @brief 设置模型项的标志
 * 
 * 该函数根据模型项的索引和其数据的可编辑性来设置模型项的标志。如果模型项所对应的数据字段是可编辑的，
 * 则为该模型项设置可选择、可编辑、可启用以及不可有子项的标志。
 * 
 * @param index 索引，指向模型中的特定项。
 * @return 返回模型项的标志，采用Qt::ItemFlags类型，描述了项的各种行为和状态。
 */
Qt::ItemFlags CTableModel::flags(const QModelIndex &index) const
{
    Qt::ItemFlags flag =  Qt::ItemIsEnabled | Qt::ItemIsSelectable; // 初始化标志为无任何标志
    auto value = index.data(Qt::UserRole); // 从指定索引处获取数据，使用Qt::UserRole作为数据角色
    auto data = qvariant_cast<QSharedPointer<IModel>>(value); // 将获取到的数据转换为IModel类型的智能指针

    // 检查数据是否非空且对应字段是否可编辑，如果是，则设置相应的标志
    if(data && data->isFieldEditable(data->getFieldNameByCol(index.column())))
        flag = flag | Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren;

    // 返回设置好的标志
    return flag;
}

/**
 * @brief 检查是否存在值描述
 * @param index 指定的数据索引
 * @param valuesDesMap 用于存储值描述的映射
 * @return 如果存在值描述，返回true，否则返回false
 */
bool CTableModel::hasValusDes(const QModelIndex &index, QMap<int, QString> &valuesDesMap) const
{
    bool bHasValueDes = false;

    // 获取存储的数据
    auto userData = index.data(Qt::UserRole);
    auto data = qvariant_cast<QSharedPointer<IModel>>(userData);
    if(!data)
        return bHasValueDes;

    // 获取字段名称
    auto fieldName = data->getFieldNameByCol(index.column());
    // 获取值描述映射
    valuesDesMap = data->getValueDesMap(fieldName);

    // 根据映射的大小判断是否存在值描述
    bHasValueDes = valuesDesMap.size()>0;

    return bHasValueDes;
}
